/* Copyright (c) [2025] [zhi1208@126.com]
 * SPDX-License-Identifier: MIT
 */

/*********************
 *      INCLUDES
 *********************/
#include <stddef.h>

#include "FreeRTOS.h"
#include "task.h"

#include "flashdb.h"

#include "bsp_at24c02.h"
#include "bsp_ws2812.h"

#include "app_hmi.h"

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void lock(fdb_db_t db);
static void unlock(fdb_db_t db);

static void led_strip_kvdb_init();

static void sw_observer_cb(lv_observer_t *observer, lv_subject_t *subject);
static void normal_observer_cb(lv_observer_t *observer, lv_subject_t *subject);
static void color_observer_cb(lv_observer_t *observer, lv_subject_t *subject);
static void rainbow_observer_cb(lv_observer_t *observer, lv_subject_t *subject);
static void hue_observer_cb(lv_observer_t *observer, lv_subject_t *subject);
static void saturation_observer_cb(lv_observer_t *observer, lv_subject_t *subject);
static void brightness_observer_cb(lv_observer_t *observer, lv_subject_t *subject);

static void led_strip_task(void *params);

/**********************
 *  STATIC VARIABLES
 **********************/
static SemaphoreHandle_t kvdb_mutex;

static uint8_t led_strip_sw_def = 0;
static uint8_t led_strip_normal_def = 0;
static uint8_t led_strip_color_def = 1;
static uint8_t led_strip_rainbow_fef = 0;
static uint16_t led_strip_hue_def = 200;
static uint8_t led_strip_saturation_def = 100;
static uint8_t led_strip_brightness_def = 100;

static struct fdb_default_kv_node led_strip_def_kv_table[] =
    {{"sw", &led_strip_sw_def, sizeof(led_strip_sw_def)},
     {"normal", &led_strip_normal_def, sizeof(led_strip_normal_def)},
     {"color", &led_strip_color_def, sizeof(led_strip_color_def)},
     {"rainbow", &led_strip_rainbow_fef, sizeof(led_strip_rainbow_fef)},
     {"hue", &led_strip_hue_def, sizeof(led_strip_hue_def)},
     {"saturation", &led_strip_saturation_def, sizeof(led_strip_saturation_def)},
     {"brightness", &led_strip_brightness_def, sizeof(led_strip_brightness_def)}};

static struct fdb_kvdb kvdb;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/
void app_led_strip_init()
{
    bsp_ws2812_init();
    led_strip_kvdb_init();

    lv_subject_add_observer(&hmi_led_strip_sw_subj, sw_observer_cb, NULL);
    lv_subject_add_observer(&hmi_led_strip_normal_subj, normal_observer_cb, NULL);
    lv_subject_add_observer(&hmi_led_strip_color_subj, color_observer_cb, NULL);
    lv_subject_add_observer(&hmi_led_strip_rainbow_subj, rainbow_observer_cb, NULL);
    lv_subject_add_observer(&hmi_led_strip_hue_subj, hue_observer_cb, NULL);
    lv_subject_add_observer(&hmi_led_strip_saturation_subj, saturation_observer_cb, NULL);
    lv_subject_add_observer(&hmi_led_strip_brightness_subj, brightness_observer_cb, NULL);

    xTaskCreate(led_strip_task, "led_strip_task", 512, NULL, 5, NULL);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
static void led_strip_kvdb_init()
{
    kvdb_mutex = xSemaphoreCreateMutex();
    vQueueAddToRegistry(kvdb_mutex, "kvdb_mutex");

    struct fdb_default_kv default_kv;
    default_kv.kvs = led_strip_def_kv_table;
    default_kv.num = sizeof(led_strip_def_kv_table) / sizeof(led_strip_def_kv_table[0]);
    fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, (void *)lock);
    fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, (void *)unlock);
    fdb_kvdb_init(&kvdb, "led_strip_config", "kvdb1", &default_kv, NULL);

    uint8_t led_strip_sw;
    uint8_t led_strip_normal;
    uint8_t led_strip_color;
    uint8_t led_strip_rainbow;
    uint16_t led_strip_hue;
    uint8_t led_strip_saturation;
    uint8_t led_strip_brightness;

    struct fdb_blob blob;

    fdb_kv_get_blob(&kvdb, "sw", fdb_blob_make(&blob, &led_strip_sw, sizeof(led_strip_sw)));
    if (blob.saved.len > 0)
        lv_subject_set_int(&hmi_led_strip_sw_subj, led_strip_sw);

    fdb_kv_get_blob(&kvdb, "normal", fdb_blob_make(&blob, &led_strip_normal, sizeof(led_strip_normal)));
    if (blob.saved.len > 0)
        lv_subject_set_int(&hmi_led_strip_normal_subj, led_strip_normal);

    fdb_kv_get_blob(&kvdb, "color", fdb_blob_make(&blob, &led_strip_color, sizeof(led_strip_color)));
    if (blob.saved.len > 0)
        lv_subject_set_int(&hmi_led_strip_color_subj, led_strip_color);

    fdb_kv_get_blob(&kvdb, "rainbow", fdb_blob_make(&blob, &led_strip_rainbow, sizeof(led_strip_rainbow)));
    if (blob.saved.len > 0)
        lv_subject_set_int(&hmi_led_strip_rainbow_subj, led_strip_rainbow);

    fdb_kv_get_blob(&kvdb, "hue", fdb_blob_make(&blob, &led_strip_hue, sizeof(led_strip_hue)));
    if (blob.saved.len > 0)
        lv_subject_set_int(&hmi_led_strip_hue_subj, led_strip_hue);

    fdb_kv_get_blob(&kvdb, "saturation", fdb_blob_make(&blob, &led_strip_saturation, sizeof(led_strip_saturation)));
    if (blob.saved.len > 0)
        lv_subject_set_int(&hmi_led_strip_saturation_subj, led_strip_saturation);

    fdb_kv_get_blob(&kvdb, "brightness", fdb_blob_make(&blob, &led_strip_brightness, sizeof(led_strip_brightness)));
    if (blob.saved.len > 0)
        lv_subject_set_int(&hmi_led_strip_brightness_subj, led_strip_brightness);
}

static void lock(fdb_db_t db)
{
    xSemaphoreTake(kvdb_mutex, portMAX_DELAY);
}

static void unlock(fdb_db_t db)
{
    xSemaphoreGive(kvdb_mutex);
}

static void sw_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    LV_UNUSED(observer);

    struct fdb_blob blob;
    uint8_t led_strip_sw = lv_subject_get_int(subject);
    fdb_kv_set_blob(&kvdb, "sw", fdb_blob_make(&blob, &led_strip_sw, sizeof(led_strip_sw)));
}

static void normal_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    LV_UNUSED(observer);

    struct fdb_blob blob;
    uint8_t led_strip_normal = lv_subject_get_int(subject);
    fdb_kv_set_blob(&kvdb, "normal", fdb_blob_make(&blob, &led_strip_normal, sizeof(led_strip_normal)));
}

static void color_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    LV_UNUSED(observer);

    struct fdb_blob blob;
    uint8_t led_strip_color = lv_subject_get_int(subject);
    fdb_kv_set_blob(&kvdb, "color", fdb_blob_make(&blob, &led_strip_color, sizeof(led_strip_color)));
}

static void rainbow_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    LV_UNUSED(observer);

    struct fdb_blob blob;
    uint8_t led_strip_rainbow = lv_subject_get_int(subject);
    fdb_kv_set_blob(&kvdb, "rainbow", fdb_blob_make(&blob, &led_strip_rainbow, sizeof(led_strip_rainbow)));
}

static void hue_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    LV_UNUSED(observer);

    struct fdb_blob blob;
    uint16_t led_strip_hue = lv_subject_get_int(subject);
    fdb_kv_set_blob(&kvdb, "hue", fdb_blob_make(&blob, &led_strip_hue, sizeof(led_strip_hue)));
}

static void saturation_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    LV_UNUSED(observer);

    struct fdb_blob blob;
    uint8_t led_strip_saturation = lv_subject_get_int(subject);
    fdb_kv_set_blob(&kvdb, "saturation", fdb_blob_make(&blob, &led_strip_saturation, sizeof(led_strip_saturation)));
}

static void brightness_observer_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    LV_UNUSED(observer);

    struct fdb_blob blob;
    uint8_t led_strip_brightness = lv_subject_get_int(subject);
    fdb_kv_set_blob(&kvdb, "brightness", fdb_blob_make(&blob, &led_strip_brightness, sizeof(led_strip_brightness)));
}

static void led_strip_task(void *params)
{
    while (1)
    {
        if (lv_subject_get_int(&hmi_led_strip_sw_subj))
        {
            if (lv_subject_get_int(&hmi_led_strip_normal_subj))
            {
                bsp_ws2812_set_all_saturation(0);
                bsp_ws2812_set_all_brightness(lv_subject_get_int(&hmi_led_strip_brightness_subj));
            }
            else if (lv_subject_get_int(&hmi_led_strip_color_subj))
            {
                bsp_ws2812_set_all_hue(lv_subject_get_int(&hmi_led_strip_hue_subj));
                bsp_ws2812_set_all_saturation(lv_subject_get_int(&hmi_led_strip_saturation_subj));
                bsp_ws2812_set_all_brightness(lv_subject_get_int(&hmi_led_strip_brightness_subj));
            }
            else if (lv_subject_get_int(&hmi_led_strip_rainbow_subj))
            {
                static int32_t offset;

                for (int16_t i = 0; i < WS2812_NUM_LEDS; i++)
                    bsp_ws2812_set_hue(i, (i * 360 / WS2812_NUM_LEDS + offset) % 360);

                bsp_ws2812_set_all_saturation(100);
                bsp_ws2812_set_all_brightness(lv_subject_get_int(&hmi_led_strip_brightness_subj));

                offset = (offset + 5) % 360;

                vTaskDelay(pdMS_TO_TICKS(30));
            }

            bsp_ws2812_update();
        }
        else
            bsp_ws2812_clear();

        vTaskDelay(pdMS_TO_TICKS(1));
    }
}
